#include <stdio.h>
#include <stdlib.h>
#include <slave.h>
#include "EPM_def.h"
#include <crts.h>
#include <simd.h>
#include <math.h>
#include <athread.h>
#include "swperf.h" 
#define M_PI  3.14159265358979323846
#define remote_ldm(coreid,ldm_var) ((unsigned long)&ldm_var | ((unsigned long)coreid << 20 ) |(1ULL << 45))

unsigned long start_entry , end_entry;

const int NN = 64;
// const int N = 64;
extern TaskDone task_done[2];
// extern  taskParam **taskQueue;
extern double EPM[64][64];
extern int capacity;
extern int32_t write_queue_ptr;
extern uint64_t write_ptr;
extern int32_t queue_num;
extern int n;

__thread_local int queue_ptr = 0;
__thread_local const uint64_t capacity_slave = 2 << 13;   //队列容量
__thread_local const int32_t queue_num_slave = 2;        //队列数量
__thread_local double Diff[2];
__thread_local int Index[2];
__thread_local_share taskParam taskQueue[2][8192]__attribute__((aligned(64))); // 由动态申请改成静态申请，
// __thread_local_share double EPM[64][64]__attribute__((aligned(64)));
__thread_local int sync = 0;
__thread_local int sync1 = 0;

__thread_local long ic, tc;
extern void penv_slave2_gld_init();
extern void penv_slave3_gst_init();
extern int data[64];

__attribute__((__always_inline__)) void check_sync(uint64_t * ptr) {
    if (*ptr == capacity) {
        // athread_syn(PP_SCOPE, 1);
        int * sync_slave0 = remote_ldm(1, sync);
        * sync_slave0 = 1;
        // end_entry = athread_stime_cycle();
        // printf("误差更新时间: %ld\n", end_entry - start_entry);
        *ptr = 0;
        write_queue_ptr = (write_queue_ptr + 1) % queue_num;  //交换队列指针
    }
}

// 实现任务打包的操作
void saesp_analysis_entry(double *target, double high, float low, int ins_id, 
                          char Q, double lValue, double rValue, int l_id, int r_id, double result) {
    taskParam *localTask = &taskQueue[write_queue_ptr][write_ptr++]; 
    localTask->target = target;
    localTask->high = high;
    localTask->low = low;

    localTask->targetRowIndex = ins_id; 
    localTask->targetColIndex = ins_id;
    localTask->ins_id = ins_id;

    // 存取微分值及其行列号
    double diff1, diff2;    // 记录微分值
    double truncErr = .0;   // 记录截断误差
    double lValue_trunc = (float)lValue;
    double rValue_trunc = (float)rValue;
    double result_high;

    switch (Q) {
        case '+': {
            diff1 = 1;
            diff2 = 1;
            result_high = lValue_trunc + rValue_trunc;
            break;
        }
        case '-': {
            diff1 = 1;
            diff2 = -1;
            result_high = lValue_trunc - rValue_trunc;
            break;
        }
        case '*': {
            diff1 = rValue;
            diff2 = lValue;
            result_high = lValue_trunc * rValue_trunc;
            break;
        } 
        case '/': {
            diff1 = 1.0 / rValue;
            diff2 = lValue * (-1.0 / (rValue * rValue));
            result_high = lValue_trunc / rValue_trunc;
            break;
        }
        case 'L': {
            diff1 = 0.0;
            diff2 = 0.0;
            result_high = result;
            break;
        }
        case 'S': {
            diff1 = cos(lValue);
            diff2 = 0;
            result_high = sin(rValue_trunc);
        }
    }

    if (l_id == 0) {
        diff1 = 0;
    }
    if (r_id == 0) {
        diff2 = 0;
    }

    localTask->Index[0] = l_id;
    localTask->Index[1] = r_id;

    localTask->Diff[0] = diff1;     
    localTask->Diff[1] = diff2;
    
    check_sync(&write_ptr);
}

// 误差更新
void run(taskParam *localTask) {
    double high = localTask->high;
    float low = localTask->low;
    int ins_id = localTask->ins_id;
    
    double * target = localTask->target;

    Diff[0] = localTask->Diff[0];
    Diff[1] = localTask->Diff[1];
    Index[0] = localTask->Index[0];
    Index[1] = localTask->Index[1];

    double truncErr = high - low;

    for (int k = 0; k < NN; k++) {
        EPM[ins_id][k] = Diff[0] * EPM[Index[0]][k] + Diff[1] * EPM[Index[1]][k];
    }
    EPM[ins_id][ins_id] += truncErr;
}

// 误差更新simd版
// void run(taskParam *localTask) {

//     double high = localTask->high;
//     float low = localTask->low;
//     int ins_id = localTask->ins_id;
    
//     double * target = localTask->target;

//     Diff[0] = localTask->Diff[0];
//     Diff[1] = localTask->Diff[1];
//     Index[0] = localTask->Index[0];
//     Index[1] = localTask->Index[1];

//     double truncErr = high - low;

//     doublev8 diff0v8, diff1v8, epm1, epm2, temp1, temp2;
//     for (int k = 0; k < NN; k+=8) {
//         diff0v8 = simd_set_doublev8(Diff[0],Diff[0],Diff[0],Diff[0],Diff[0],Diff[0],Diff[0],Diff[0]);
//         diff1v8 = simd_set_doublev8(Diff[1],Diff[1],Diff[1],Diff[1],Diff[1],Diff[1],Diff[1],Diff[1]);
//         simd_load(epm1, &EPM[Index[0]][k]);
//         simd_load(epm2, &EPM[Index[1]][k]);
//         temp1 = simd_vmuld(diff1v8,epm2);
//         temp2 = simd_vmad(diff0v8, epm1, temp1);
//         simd_store(temp2, &EPM[ins_id][k]);
//     }
//     EPM[ins_id][ins_id] += truncErr;
// }


void saesp_task_finished() {
    task_done[write_queue_ptr].terminate = true;
    task_done[write_queue_ptr].remain_task = write_ptr;
    // athread_syn(PP_SCOPE, 1);    //设置同步点
    int * sync_slave1 =remote_ldm(0, sync1);
    *sync_slave1  =1;
}

void func()
{   
    int tid = (int)athread_get_tid(); 
    unsigned long start, end;
    if(tid == 1){
        int tid1 = (int)athread_get_tid(); 
        // printf("tid1 : %d\n",tid1);
        // athread_syn(PP_SCOPE, 0);
        while(sync == 1);
        while (1) {
            flush_slave_cache();
            flush_slave_cache();
            if (task_done[queue_ptr].terminate) {
                break;
            }
            // start = athread_stime_cycle();
            // penv_slave2_gld_count(&ic); 
            // penv_slave3_gst_count(&ic);
            for (int32_t i = 0; i < capacity_slave; i++){
                taskParam *localTask = &taskQueue[queue_ptr][i];
                run(localTask);
            }
            // penv_slave2_gld_count(&tc);
            // penv_slave3_gst_count(&tc);
            // data[tid] = tc -ic;
            // end = athread_stime_cycle();
            // printf("误差更新需的时间: %ld\n",end - start);
            // if(tid == 1){
            //     printf("\n-------------------tid 1 gld result-----------------------------------------\n\n");

            //     for(int i = 0 ; i < 64; i++){
            //         printf("information of gld : %ld\n", data[tid]);
            //     }

            //     printf("\n-------------------tid 1 -----------------------------------------\n\n");
            // }
            queue_ptr = (queue_ptr + 1) % queue_num_slave;
            // athread_syn(PP_SCOPE, 0);
            while(sync1 == 1);
            // int * sync_slave1 =remote_ldm(0, sync1);
            // *sync_slave1  =1;
        }
        for (int32_t i = 0; i < task_done[queue_ptr].remain_task; i++) {
            taskParam *localTask = &taskQueue[queue_ptr][i];
            run(localTask);
        }
    }
    else{
        double a, b, h, s1, x, pi, tmp, x_pi, sin_x_pi, fa, fb;
        double temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9;

        int tid1 = (int)athread_get_tid(); 
        // printf("tid0 : %d\n",tid1);
        // start_entry = athread_stime_cycle();
        a = 0.0;
        saesp_analysis_entry(&a, a, (float)a, 1, 'L', 0, 0, 0, 0, a);
        b = 1.0;
        saesp_analysis_entry(&b, b, (float)b, 2, 'L', 0, 0, 0, 0, b);
        pi = M_PI;
        saesp_analysis_entry(&pi, pi, (float)pi, 3, 'L', 0, 0, 0, 0, pi);
        temp1 = b / (2.0 * n);
        saesp_analysis_entry(&temp1, temp1, (float)temp1, 4, '/', b, (2.0 * n), 2, 0, temp1);
        temp2 = a / (2.0 * n);   
        saesp_analysis_entry(&temp2, temp2, (float)temp2, 5, '/', a, (2.0 * n), 1, 0, temp2);
        h = temp1 - temp2;
        saesp_analysis_entry(&h, h, (float)h, 6, '-', temp1, temp2, 4, 5, h); 
        x = a;
        saesp_analysis_entry(&x, x, (float)x, 7, '+', 0, a, 0, 1, x);
        x_pi = a * pi;
        saesp_analysis_entry(&x_pi, x_pi, (float)x_pi, 8, '*', a, pi, 1, 3, x_pi);
        sin_x_pi = sin(x_pi) + 0;
        saesp_analysis_entry(&sin_x_pi, sin_x_pi, (float)sin_x_pi, 9, 'S', x_pi, 0, 8, 0, sin_x_pi); 
        // fa = sin_x_pi;  这里赋值加了个0
        fa = 0 + sin_x_pi;
        saesp_analysis_entry(&fa, fa, (float)fa, 10, '+', 0, sin_x_pi, 0, 9, fa);
        x_pi = b * pi;
        saesp_analysis_entry(&x_pi, x_pi, (float)x_pi, 11, '*', b, pi, 2, 3, x_pi);
        sin_x_pi = sin(x_pi);
        saesp_analysis_entry(&sin_x_pi, sin_x_pi, (float)sin_x_pi, 12, 'S', x_pi, 0, 11, 0, sin_x_pi);
        // fb = sin_x_pi;  这里赋值加了个0
        fb = 0 + sin_x_pi;
        saesp_analysis_entry(&fb, fb, (float)fb, 13, '+', 0, sin_x_pi, 0, 12, fb);
        s1 = fa + fb;
        saesp_analysis_entry(&s1, s1, (float)s1, 14, '+', fa, fb, 10, 13, s1);// 这里是对的 和原始simpsons相比
        temp3 = pi * a;
        saesp_analysis_entry(&temp3, temp3, (float)temp3, 15, '*', pi, a, 3, 1, temp3);
        temp4 = pi * b;
        saesp_analysis_entry(&temp4, temp4, (float)temp4, 16, '*', pi, b, 3, 2, temp4);
        temp5 = sin(temp3);
        saesp_analysis_entry(&temp5, temp5, (float)temp5, 17, 'S', temp3, 0, 15, 0, temp5); 
        temp6 = sin(temp4);
        saesp_analysis_entry(&temp6, temp6, (float)temp6, 18, 'S', temp4, 0, 16, 0, temp6); 
        s1 = temp5 + temp6;
        saesp_analysis_entry(&s1, s1, (float)s1, 19, '+', temp5, temp6, 17, 18, s1);
        // s1 = sin(pi * a) + sin(pi * b);
        for (int l = 0; l < n; l++) { // ITERS before
            x += h;                                                                   
            saesp_analysis_entry(&x, x, (float)x, 20, '+', x, h, 7, 6, x);
            x_pi = x * pi;
            saesp_analysis_entry(&x_pi, x_pi, (float)x_pi, 21, '*', x, pi, 20, 3, x_pi);
            sin_x_pi = sin(x_pi);
            saesp_analysis_entry(&sin_x_pi, sin_x_pi, (float)sin_x_pi, 22, 'S', x_pi, 0, 21, 0, sin_x_pi);     // sin
            // s1 = s1 + 4.0 * sin_x_pi;    
            temp7 = 4.0 * sin_x_pi;
            saesp_analysis_entry(&temp7, temp7, (float)temp7, 23, '*', 4.0, sin_x_pi, 0, 22, temp7);
            s1 = s1 + temp7;
            saesp_analysis_entry(&s1, s1, (float)s1, 24, '+', s1, temp7, 19, 23, s1);
            x = x + h;
            saesp_analysis_entry(&x, x, (float)x, 25, '+', x, h, 20, 6, x);
            x_pi = x * pi;
            saesp_analysis_entry(&x_pi, x_pi, (float)x_pi, 26, '*', x, pi, 25, 3, x_pi);
            sin_x_pi = sin(x_pi);
            saesp_analysis_entry(&sin_x_pi, sin_x_pi, (float)sin_x_pi, 27, 'S', x_pi, 0, 26, 0, sin_x_pi);  // sin
            // s1 = s1 + 2.0 * sin_x_pi;
            temp8 = 2.0 * sin_x_pi;
            saesp_analysis_entry(&temp8, temp8, (float)temp8, 28, '*', 2.0, sin_x_pi, 0, 27, temp8);
            s1 = s1 + temp8;   
            saesp_analysis_entry(&s1, s1, (float)s1, 29, '+', s1, temp8, 24, 28, s1);   // 这里就完全不对了
        }
        s1 = s1 - fb;
        saesp_analysis_entry(&s1, s1, (float)s1, 30, '-', s1, fb, 29, 13, s1);
        // tmp = h * M_PI / 3.0;
        temp9 = h * M_PI;
        saesp_analysis_entry(&temp9, temp9, (float)temp9, 31, '*', h, M_PI, 6, 0, temp9);
        tmp = temp9 / 3.0;
        saesp_analysis_entry(&tmp, tmp, (float)tmp, 32, '/', temp9, 3.0, 31, 0, tmp);
        s1 = s1 * tmp;
        saesp_analysis_entry(&s1, s1, (float)s1, 33, '*', s1, tmp, 30, 32, s1);
        saesp_task_finished();
    }
}
